package base.thread_demo.demo09;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程范围内数据共享： 共享数据有多个，需要封装成一对象MyThreadScopeData
 *
 * 线程t1、t2 和模块A、B 分别需要拿到对象 MyThreadScopeData，因此将MyThreadScopeData内部通过封装ThreadLocal，
 * 来返回跟线程绑定的 MyThreadScopeData对象。这就好比数据库连接池，事务的提交总是提交的是跟当前事务关联的connection对象。
 * 但是，你不能把MyThreadScopeData设计成单例，单例就会将所有线程共享MyThreadScopeData，
 * 而不是每个线程只共享线程范围内的MyThreadScopeData
 */
public class ThreadScopeShareData3 {
    private static final Logger log = LoggerFactory.getLogger(ThreadScopeShareData3.class);

    public static void main(String[] args) {
        new Thread(()->{
            MyThreadScopeData instance = MyThreadScopeData.getThreadInstance();
            instance.setName("张三");
            instance.setAge(18);
            log.info("{} has put data :{}", Thread.currentThread().getName(), instance);

            new ThreadScopeShareData3().new A().get();
            new ThreadScopeShareData3().new B().get();
        },"t1").start();

        new Thread(()->{
            MyThreadScopeData instance = MyThreadScopeData.getThreadInstance();
            instance.setName("LiSi");
            instance.setAge(19);
            log.info("{} has put data :{}", Thread.currentThread().getName(), instance);

            new ThreadScopeShareData3().new A().get();
            new ThreadScopeShareData3().new B().get();
        },"t2").start();
    }


    class A {
        private Logger log = LoggerFactory.getLogger(A.class);
        public void get(){
            MyThreadScopeData instance = MyThreadScopeData.getThreadInstance();
            log.info("A:{}-> {}", Thread.currentThread().getName(),instance);        }
    }

    class B {
        private Logger log = LoggerFactory.getLogger(B.class);
        public void get(){
            MyThreadScopeData instance = MyThreadScopeData.getThreadInstance();
            log.info("B:{}-> {}", Thread.currentThread().getName(),instance);
        }
    }
}

class MyThreadScopeData {
    private String name;
    private int age;
    // local 用于存储 MyThreadScopeData 对象的集合
    private static ThreadLocal<MyThreadScopeData> local = new ThreadLocal<>();

    private MyThreadScopeData(){}

    /**
     * 这个方法有点跟单例模式相似,但是我们获取的是跟线程相关的共享实例,
     * 多个线程之间并不需要共享一个MyThreadScopeData实例.因此不能够设计成单例模式.
     */
    public static /*synchronized*/ MyThreadScopeData getThreadInstance(){
        MyThreadScopeData instance = local.get();
        if(instance == null){
            instance = new MyThreadScopeData();
            local.set(instance);//注意:这里别忘记往 local 中设置值
        }
        return instance;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "MyThreadScopeData{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
